package com.dreamlike.loomdemo;

import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import org.springframework.web.context.request.async.DeferredResult;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class ControllerInterceptor {
    private Object target;
    private HashMap<String, List<Method>> targetMethods;

    public ControllerInterceptor(Object target) {
        this.target = target;
        targetMethods = new HashMap<>();
        for (Method method : target.getClass().getMethods()) {
            List<Method> methodList = targetMethods.get(method.getName());
            if(methodList == null){
                final ArrayList<Method> list = new ArrayList<>(2);
                list.add(method);
                targetMethods.put(method.getName(),list);
            }else {
                methodList.add(method);
            }
        }
    }
    @RuntimeType
    public Object interceptor(@Origin Method method, @AllArguments Object[] args) throws InvocationTargetException, IllegalAccessException {
        DeferredResult<Object> result = new DeferredResult<Object>();
        Thread.startVirtualThread(()->{
            try {
                Object res = getTargetMethod(method).invoke(target, args);
                result.setResult(res);
            } catch (Throwable e) {
                result.setErrorResult(e);
            }
        });
        return result;
    }
    private Method getTargetMethod(Method proxy){
        final List<Method> list = targetMethods.get(proxy.getName());
        if (list == null) return null;
        Class<?>[] proxyParameters = proxy.getParameterTypes();
        for (Method method : list) {
            if (equalParamTypes(proxyParameters, method.getParameterTypes())){
                return method;
            }
        }
        return null;
    }
    private boolean equalParamTypes(Class<?>[] params1, Class<?>[] params2) {
        if (params1.length == params2.length) {
            for (int i = 0; i < params1.length; i++) {
                if (params1[i] != params2[i])
                    return false;
            }
            return true;
        }
        return false;
    }
}
